<?php
/**
 * zen_fabrica_gtk.php
 * @author Juan Belon
 * @access public
 * @copyright LGPL, GPL
 * @package zenphp_generador
 * @version 0.1.1
 * @uses zenphp FrameWork
 * @link http://csl2-zenphp.forja.rediris.es
 * @link http://www.zenphp.es
 * @link https://forja.rediris.es/projects/csl2-zenphp/
 * @magic Clase que fabrica los componentes de la aplicación generadora de aplicaciones
 */
// +----------------------------------------------------------------------
// | zenphp.es
// +----------------------------------------------------------------------
class zen_fabrica_gtk {
	/**
	* La ventana principal a la que se asocian todos los componentes
	* @var zen_ventana_principal
	*/
	private $ventana_principal;
	/**
	* Colores para colorear el código si es PHP
	* @var array
	*/
	static protected $colores = array("blue", "red", "orange", "darkgreen", "black");
	/**
	* Constructor de la fábrica Gtk
	* @return zen_fabrica_gtk
	*/
	public function __construct() {}
	/**
	* La función que hace posible el singleton gracias a la propiedad estática de una variable
	*/
	public function obtenerFabrica($ventana_principal) {
    static $instancia;
    if (!isset($instancia)) {
      $instancia =& new zen_fabrica_gtk();
    }
    $instancia->ventana_principal =& $ventana_principal;
    if (!isset($ventana_principal->eventos)) 
      $eventos = new zen_eventos_ventana_principal($ventana_principal);
      $ventana_principal->eventos =& $eventos;
    return $instancia;
	}
	/**
	* Devuelve una imagen leyéndola desde un fichero
	* @return GtkImage
	*/
	public function icono($img=null) {
    if (defined($img)) {
      return GtkImage::new_from_stock(constant($img), 1);
    } else if (is_file($img) && is_readable($img)) {
      return GtkImage::new_from_file($img);
    } else {
      if (ZF_GTK_MODO_DEBUG)
        echo "\nNo se puede leer la imagen '".$img."'\n";
      return new GtkImage();
    }
	}
	/**
	* Devuelve un nuevo item de tipo GtkMenuItem con su texto
	* @param str $mostrar
	* @return GtkMenuItem
	*/
  public function menuItem($mostrar) {
    return new GtkMenuItem($mostrar);
	}
	/**
	* Devuelve un nuevo item de tipo GtkMenuItem con su id de imagen
	* @param str $mostrar
	* @return GtkImageMenuItem
	*/
  public function imagen_menuItem($mostrar) {
    //__construct ([stock_id], [GtkAccelGroup accel_group])
    return new GtkImageMenuItem($mostrar);
	}
	/**
	* Devuelve un nuevo GtkSeparatorMenuItem
	* @return GtkSeparatorMenuItem
	*/
	public function separadorItem() {
	 return new GtkSeparatorMenuItem();
	}
	/**
	* Devuelve un nuevo GtkMenu
	* @return GtkMenu
	*/
	public function menu() {
	 return new GtkMenu();
	}
	/**
	* Devuelve un nuevo enganche de menú: GtkTearoffMenuItem
	* @return GtkTearoffMenuItem
	*/
  public function enganche() {
    return new GtkTearoffMenuItem();
	}
	
	/**
	* Construye los menús a partir de un fichero XML y los asocia a la ventana principal
	*/ 
	public function asociar_menus(){
    $this->ventana_principal->menu =& new zen_menu_gtk($this->ventana_principal);
    $this->menu =& $this->ventana_principal->menu;
    $this->menu->crear_desde_xml("zen_generador.xml");
    $this->menu->set_size_request(135,24);
    $e =& $this->ventana_principal->eventos;
    $this->ventana_principal->connect_simple('destroy', array('Gtk', "main_quit"));
    foreach($this->menu->nombres_menus as $menu){
      $this->menus[$menu] = $this->menu->obtenerWidget($menu);
      $this->menus[$menu]->connect_simple("activate", array($e,"evento_menu"), $menu);
    }
    $this->ventana_principal->contenedor->pack_start($this->menu,false,false,false);
	}

	
	/**
	* Genera la interfaz del generador con un panel 
	*/
	public function &crear_navegador()
	{
    $w =& $this->ventana_principal->widgets; //para reducir el nombre ;)
    $e =& $this->ventana_principal->eventos; //Los eventos igual :]
    $w['navegador'] =& new GtkVBox();
    $w['navegador']->pack_start(new GtkLabel('Doble click para abrir el cartucho, o seleccionarlo y pulsar ENTER'), false);

    $w['panel_horizontal'] =& new GtkHPaned();
    $w['panel_horizontal']->set_position(200);
    //Cartuchos:
    //nombre del fichero, selección, ruta completa, comprobar si es directorio, imagen de previsualización
    $w['fichero_seleccionado'] =& new GtkListStore(GObject::TYPE_STRING, GObject::TYPE_STRING, GObject::TYPE_STRING, GObject::TYPE_BOOLEAN, GdkPixbuf::gtype);
    $w['fichero_seleccionado']->set_sort_column_id(0, Gtk::SORT_ASCENDING);

    $w['vista_arbol'] =& new GtkTreeView($w['fichero_seleccionado']);
    $w['columna_vista_arbol'] =& new GtkTreeViewColumn(
      'Imagen',
      new GtkCellRendererPixbuf(),
      'pixbuf',
      4
    );
    $texto =& new GtkCellRendererText();
    $w['columna_vista_arbol']->pack_start($texto);
    $w['columna_vista_arbol']->add_attribute($texto, 'markup', 1);
    $w['vista_arbol']->append_column($w['columna_vista_arbol']);
    $w['vista_arbol']->set_headers_visible(false);
    $w['vista_arbol']->connect('key-press-event', array($e, 'seleccion_fichero'));
    $w['vista_arbol']->connect('button-press-event', array($e, 'seleccion_fichero'));
    $this->ventana_principal->cargar_ficheros($w['fichero_seleccionado'], getcwd());
    $w['lista_ficheros'] =& new GtkScrolledWindow();
    $w['lista_ficheros']->add($w['vista_arbol']);
    $w['lista_ficheros']->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    $w['navegador_fichero'] =& new GtkVBox();
    $w['navegador_fichero']->pack_start($w['lista_ficheros']);
    $w['comprobar_inclusion_zf'] =& new GtkCheckbutton('Incluir el motor zenphp en el directorio de salida');
    $w['comprobar_inclusion_zf']->set_active(true);
    $w['comprobar_inclusion_zf']->connect('toggled', array($e, 'marcar_inclusion'));
    $w['navegador_fichero']->pack_start($w['comprobar_inclusion_zf'], false);
    $w['panel_horizontal']->add1($w['navegador_fichero']);
    $w['imagen'] =& new GtkImage();
    $w['bloc_notas'] =& new GtkNotebook();
    $w['frame_1_bloc_notas'] = & new GtkScrolledWindow();
    $w['frame_2_bloc_notas'] = & new GtkFrame("Configurar cartucho (leido del XML)");
    $w['frame_3_bloc_notas'] = & new GtkFrame("Detalles del valor seleccionado");
    $w['frame_4_bloc_notas'] = & new GtkFrame("Acerca...");
    $w['frame_1_bloc_notas']->add_with_viewport($w['imagen']);
    $w['frame_1_bloc_notas']->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    $w['arbol_propiedades_cartucho'] =& new GtkTreeView($this->crear_modelo_arbol('propiedades',"modelo_propiedades_cartucho"));
    //Fix: se ha cambiado el GtkTreeView por un GtkTkClist
    //$w['arbol_propiedades_basedatos']=& new GtkTreeView($this->crear_modelo_arbol('basedatos'));
    $nombres_bd = array("Icono","Servidor","Usuario","Password","Nombre","Tipo");
    @$w['arbol_propiedades_basedatos'] =& new GTkClist(count($nombres_bd),$nombres_bd);
    $this->configurar_arbol($w['arbol_propiedades_cartucho']);
    //$this->configurar_arbol($w['arbol_propiedades_basedatos'],'basedatos');
    $w['arbol_propiedades_basedatos']->connect("select-row", array($e,'seleccion_bd'));
    $v = new GtkVBox();
    $v->pack_start(new GtkLabel('Contenido del cartucho'), false);
    $v->add( $this->ensartar('barras',$w['arbol_propiedades_cartucho']));
    $v->pack_start(new GtkLabel('Bases de datos'), false);
    $v->add( $this->ensartar('barras',$w['arbol_propiedades_basedatos']));
    $w['frame_2_bloc_notas']->add($v);
    list($w['caja_codigo'],$w['buffer_codigo']) = $this->crear_caja_texto_fuente();
    $w['frame_3_bloc_notas']->add($this->ensartar('barras',$w['caja_codigo']));
    $w['frame_4_bloc_notas']->add(new GtkLabel('Acerca del cartucho'));
    $w['bloc_notas']->append_page($w['frame_1_bloc_notas'], new GtkLabel("Imagen previsualiza"));
    $w['bloc_notas']->append_page($w['frame_2_bloc_notas'], new GtkLabel("Configurar cartucho"));
    $w['bloc_notas']->append_page($w['frame_3_bloc_notas'], new GtkLabel("Detalles..."));
    $w['bloc_notas']->append_page($w['frame_4_bloc_notas'], new GtkLabel("Acerca de..."));	
    $w['panel_horizontal']->add2($w['bloc_notas']);
    $w['navegador']->pack_end($w['panel_horizontal']);
    return $w['navegador'];
	}
	/**
	* Ensarta un componente en una ventana del tipo: 'barra',..., y la devuelve
	* @param str $tipo
	* @param Object $componente
	*/
	private function ensartar($tipo,&$componente){
    switch ($tipo){
      default: case 'barras': //Ambas Barras de scroll
        $ventana = new GtkScrolledWindow();
        $ventana->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
        $ventana->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
        $ventana->add($componente);
        break;
      }
    return $ventana;
	}
	/**
	* Construye el árbol de propiedades leidas del XML asignándole 
	* las funciones adecuadas a las columnas y celdas.
	* Toma el objeto $this->widgets['arbol_propiedades_cartucho'] 
	* que es un GtkTreeView basado en el $this->crear_modelo_arbol
	* @param GtkTreeView &$vista_arbol
	* @param str $tipo
	*/
	private function configurar_arbol(&$vista_arbol,$tipo='propiedades'){
    $columna = new GtkTreeViewColumn();
    $e = $this->ventana_principal->eventos; //Asociación de eventos
    switch ($tipo){
      default: case 'propiedades':
        $columna->set_title('Icono y tipo');
        $celda = new GtkCellRendererPixbuf();
        $columna->pack_start($celda, false);
        $columna->set_attributes($celda, 'stock-id', 1);
        $celda = new GtkCellRendererText();
        $columna->pack_start($celda, true);
        $columna->set_cell_data_func($celda,array($e, 'poner_etiqueta_arbol_tipo'));
        $vista_arbol->append_column($columna);
        $celda = new GtkCellRendererText();
        $vista_arbol->insert_column_with_data_func(-1, 'Aplicacion', $celda, array($e,'poner_etiqueta_arbol_nombre'));
        $celda = new GtkCellRendererText();
        $vista_arbol->insert_column_with_data_func(-1, 'Contenido', $celda, array($e,'poner_etiqueta_arbol_id'));
        $celda = new GtkCellRendererText();
        $vista_arbol->insert_column_with_data_func(-1, 'Nombre|Valor', $celda,array($e, 'poner_etiqueta_arbol_valor'));
        /*$elemento = new elemento_configuracion_arbol();
        $elemento->etiqueta_tipo = new GtkLabel();
        $elemento->etiqueta_nombre = new GtkLabel();
        $elemento->etiqueta_id = new GtkLabel();
        $elemento->etiqueta_valor = new GtkLabel();
        $elemento->icono_imagen = new GtkImage();
        $vista_arbol->set_data('elemento', $elemento);*/
        break;
    }
    $vista_arbol->get_selection()->set_mode(Gtk::SELECTION_SINGLE);
    $vista_arbol->get_selection()->connect('changed', array($e, 'propiedades_cartucho_cambian'));
	}
	/**
	* Crea un modelo GtkListStore para el constructor de un GtkTreeView
	* Los tipos pueden ser: propiedades, basedatos,
	* @param str $tipo
        * @param str $nombre_store
	* @return GtkListStore
	*/
	private function crear_modelo_arbol($tipo,$nombre_store){
   $w =& $this->ventana_principal->widgets;
	 $w[$nombre_store] =& new GtkListStore(GObject::TYPE_PHP_VALUE, GObject::TYPE_STRING);
	 switch ($tipo){
	  case 'propiedades':
	   //$ids = Gtk::stock_list_ids(); sort($ids);
     break;
	 case 'basedatos':
	
	   break;
	 }
	
	 return $w[$nombre_store];
	}
	/**
	* Función que permite añadir una fila al modelo de un árbol GtkTreeView
	* @param array $fila Son las propiedades de la fila a añadir, su icono y demás...
	* @param str $tipo Es el tipo de árbol de la ventana principal
	* @param str $nombre_store El nombre para el objeto que guarda el modelo (GtkListStore) creado en $this->crear_modelo_arbol(...)
	*/
  public function anadir_filas_arbol($fila,$tipo="propiedades",$nombre_store="modelo_propiedades_cartucho"){
    $store =& $this->ventana_principal->widgets[$nombre_store];
    switch ($tipo){
     default: case 'propiedades':
    	if (!isset($fila['id'])) $fila['id'] = Gtk::STOCK_HELP;
    	$info  =  new info_configuracion_arbol($fila['id'],$fila['tipo']);
	    /** @var GtkIconSet $icon_set */
	    $icon_set = GtkIconFactory::lookup_default($fila['id']);
	    $tamanios = array();
	    $tamano   = 0;
	    if ($icon_set) {
	      $tamanios = $icon_set->get_sizes(); //devuelve un array ;)
	      $tamano   = $tamanios[0];
	      $n = count($tamanios);
	      for ($i = 0; $i < $n; $i++) {
	        if ($tamanios[$i] == Gtk::ICON_SIZE_MENU) {
	          $tamano = Gtk::ICON_SIZE_MENU;
	          break;
	        }
	      }
	      $info->icono = $this->ventana_principal->render_icon($info->stock_id, $tamano);
	      if ($tamano != Gtk::ICON_SIZE_MENU) {
	        list($ancho, $alto) = Gtk::icon_size_lookup(Gtk::ICON_SIZE_MENU);
	        $info->icono = $info->icono->scale_simple($ancho, $alto, 'bilinear');
	      }
	    } else {
	      $info->icono = null;
	    }
	    $info->contenido = array($fila['contenido'], $fila['nombre'], $tamanios, $tamano, $fila['id']);

	    if (!isset($fila['valor']) || is_null($fila['valor'])) {
	      $info->valor = '';
	    } else {
	      $info->valor = $fila['valor'];
	    }
	    $iter = $store->append();
	    $store->set($iter, 0, $info, 1, $fila['id']);
	    break;
	  }
  }
	/**
	* Crea un Text View con un buffer para el código fuente a editar,que se puede llamar
	*  desde cada componente de las opciones del árbol de propiedades del cartucho en 
	*  la ventana principal.
	* @return array
	*/
	private function crear_caja_texto_fuente(){
	if (class_exists('GtkSourceView')){
     $texto  =& new GtkSourceView();
     $buffer =& new GtkSourceBuffer();
     $texto->set_show_line_numbers(true);
     $texto->set_show_line_markers(true);
     $texto->set_show_line_markers(true);
	} else {
	 $texto  =& new GtkTextView();
	 $buffer =& new GtkTextBuffer();
	}
	
	
    
    $texto->set_buffer($buffer);
    $texto->set_editable(true);
    $texto->set_cursor_visible(true);
    
    $tag_table = $buffer->get_tag_table();	
    foreach(self::$colores as $color) {
      $tag = new GtkTextTag($color);
      $tag->set_property("foreground", $color);
      $tag_table->add($tag);
    }
    
    return array($texto, $buffer);
	}
	
	/**
	* Colorea el código:
	* @param str $fichero
	* @param bool $highlight
	* @param GtkSourceBuffer $sourcebuffer
	*/
	public function highlightSource($fichero, $highlight=true,$sourcebuffer)
    {
        $filecontents = file_get_contents($fichero);

        $highlighted = '';
        $color_codes = array();

        if ($highlight && extension_loaded('tokenizer')) {
			$tokens = token_get_all($filecontents);

            // this would be much easier if we had functions which supported varargs (like insert_with_tags_by_name), but improvised solution works in the meantime.

            foreach ($tokens as $token) {
            $start = strlen($highlighted);
                if (is_string($token)) {
                    //single string
                    if($token == "(" || $token == ")" || $token == "{" || $token == "}" || $token == ";" || $token == "@" || $token == "[" || $token == "]" || $token == "!") {
                        $color = "darkgreen";
                    }
                    else {
                        $color = "black";
                    }
                    $highlighted .= $token;
                } else {
                    list($type, $value) = $token;
                    switch($type) {
                        case T_COMMENT:
                        case T_DOC_COMMENT: {
                            $color = "orange";
                            break;
                        }
                        case T_CLASS:
                        case T_DOUBLE_ARROW:
                        case T_NEW:
                        case T_PAAMAYIM_NEKUDOTAYIM:
                        case T_IF:
                        case T_OBJECT_OPERATOR:
                        case T_RETURN:
                        case T_EXTENDS:
                        case T_CLASS_C:
                        case T_FUNC_C:
                        case T_WHILE:
                        case T_EXIT:
                        case T_INC:
                        case T_IS_EQUAL:
                        case T_IS_EQUAL:
                        case T_IS_IDENTICAL:
                        case T_IS_NOT_EQUAL:
                        case T_IS_NOT_IDENTICAL:
                        case T_IS_SMALLER_OR_EQUAL:
                        case T_MINUS_EQUAL:
                        case T_MOD_EQUAL:
                        case T_MUL_EQUAL:
                        case T_OR_EQUAL:
                        case T_PLUS_EQUAL:
                        case T_FOREACH:
                        case T_AS:
                        case T_FOR:
                        case T_DO:
                        case T_CONTINUE:
                        case T_BREAK:
                        case T_SWITCH:
                        case T_CASE:
                        case T_LIST:
                        case T_PRINT:
                        case T_ECHO:
                        case T_ARRAY_CAST:
                        case T_BOOL_CAST:
                        case T_DOUBLE_CAST:
                        case T_INT_CAST:
                        case T_OBJECT_CAST:
                        case T_STRING_CAST:
                        case T_UNSET_CAST:
                        case T_UNSET:
                        case T_INCLUDE:
                        case T_INCLUDE_ONCE:
                        case T_REQUIRE:
                        case T_REQUIRE_ONCE:
                        case T_ELSE:
                        case T_ELSEIF:
                        case T_GLOBAL:
                        case T_ARRAY:
                        case T_BOOLEAN_AND:
                        case T_BOOLEAN_OR:
                        case T_CONCAT_EQUAL:
                        case T_DIV_EQUAL:
                        case T_CONST:
                        case T_STATIC:
                        case T_AND_EQUAL:
                        case T_FUNCTION: {
                            $color = "darkgreen";
                            break;
                        }
                        case T_LNUMBER:
                        case T_CLONE:
                        case T_OPEN_TAG:
                        case T_CLOSE_TAG:
                        case T_NUM_STRING:
                        case T_DNUMBER:
                        case T_VARIABLE:
                        case T_PRIVATE:
                        case T_PUBLIC:
                        case T_PROTECTED:
                        case T_VAR:
                        case T_INSTANCEOF:
                        case T_STRING: {
                            $color = "blue";
                            break;
                        }
                        case T_CONSTANT_ENCAPSED_STRING: {
                            $color = "red";
                            break;
                        }
                        default: {
                            $color = "black";
                            break;
                        }
                    }
                    $highlighted .= $value;
                }
                $end = strlen($highlighted);
                $color_codes[$color][] = $start . ":" . $end;
            }
            $sourcebuffer->set_text($highlighted);
            foreach($color_codes as $color_code=>$positions) {
                foreach($positions as $position) {
                    list($start_pos,$end_pos) = explode(":", $position);
                    $start = $sourcebuffer->get_iter_at_offset($start_pos);
                    $end = $sourcebuffer->get_iter_at_offset($end_pos);
                    $sourcebuffer->apply_tag_by_name($color_code, $start, $end);
                }
            }
        } else {
            $sourcebuffer->set_text($filecontents);
        }
    }//protected function highlightSource($fichero)
    /**
     * Muestra un cuadro de diálogo para preguntar algo y después devuelve true o false
     * en función de lo que el usuario haya elegido
     * @param array|str $tipo sirve para especificar por el programador el array para crear el GtkDialog o bien : "pregunta","aceptacion"
     * @param str $titulo para el título de la ventana
     * @param str $pregunta es la pregunta que se le hace al usuario, de tipo si o no
     */
    function cuadro_dialogo($tipo="pregunta",$titulo="Esto es cuadro de dialogo Gtk",$pregunta="Te gusta Gtk2?"){
	 if (!is_array($tipo)){
	  switch ($tipo){
		default: case 'pregunta':
			$tipo = array(
			 Gtk::STOCK_NO, Gtk::RESPONSE_NO,
			 Gtk::STOCK_YES, Gtk::RESPONSE_YES
			);
			break;
		case 'aceptacion':
			$tipo = array(Gtk::STOCK_OK, Gtk::RESPONSE_OK);
			break;
	  }
	 }
	 $dialogo = new GtkDialog($titulo,NULL,Gtk::DIALOG_MODAL,$tipo);
 
	 /* Crear y añadir la pregunta al cuadro de diálogo */
	 $etiqueta= new GtkLabel($pregunta);
	 $area_superior = $dialogo->vbox;
	 $area_superior->add($etiqueta);
 	 
	 /* Mostrar todos los widgets añadidos */
	 $dialogo->show_all();
 	 
	 /* Ejecutar el cuadro de diálogo */
	 $resultado = $dialogo->run();
	 //Ya podemos destruir el cuadro:
	 $dialogo->destroy();
	 //Por medio del resultado obtenemos la variable 
	 // de respuesta pulsada por el usuario
	 switch($resultado) {
	      case (Gtk::RESPONSE_YES||Gtk::RESPONSE_OK):
        	return true;
        	break;
    	case (Gtk::RESPONSE_NO):
	        return false;
        	break;
        default:
        	return false;
        	break;
	 }
	}
}
?>